package cn.stylefeng.guns.sys.core.upay.utils;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.binary.Base64;
import java.io.FileInputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.*;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * TODO 加解密相关工具
 * 
 * @author jcouyang@chinaums.com
 * @date 2017年5月3日 上午10:34:37
 */
public abstract class CipherUtils {
	//private static final Logger logger = org.slf4j.LoggerFactory.getLogger(CipherUtils.class);
	private static final String ALGORITHM_MD5 = "MD5";
	private static final String ALGORITHM_RSA = "RSA";
	private static final String ALGORITHM_SIGN = "SHA256withRSA";
	private static final String ENCODING = "UTF-8";
	private static final String KEY_ALGORITHM_RSA = "RSA";
	private static final String KEY_SIGNATURE_RSA = "SHA256WithRSA";
	private static final String KEY_SIGNATURE_RSA1 = "SHA1WithRSA";

	/**
	 * @param src
	 * @return
	 */
	public static String md5(String src) {
		return md5(src, true);
	}

	/**
	 * @param src
	 * @param upcase
	 *            结果是否大写true大写，false小写
	 * @return
	 */
	public static String md5(String src, boolean upcase) {
		String ret = null;

		try {
			MessageDigest md5 = MessageDigest.getInstance(ALGORITHM_MD5);
			byte[] bytes = md5.digest(src.getBytes("UTF-8"));

			ret = StringUtils.bytes2HexString(bytes, upcase);

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		return ret;
	}

    /**
     * 使用RSAWith256进行签名
     * @param source 待签名的字符串
     * @param privateKeyStr 私钥
     * @param charset 字符编码
     * @return
     * @throws Exception
     */
    public static String sign(String source, String privateKeyStr, String charset) throws Exception{
        PrivateKey privateKey = getPrivateKey(privateKeyStr);

//        logger.debug("参与加密的明文：");
//        logger.debug(source);

        final Signature signatureChecker = Signature.getInstance(ALGORITHM_SIGN);
        signatureChecker.initSign(privateKey);
        signatureChecker.update(source.getBytes(charset));
        String signature = Base64.encodeBase64String(signatureChecker.sign());
//        String signature = Base64.encode(signatureChecker.sign());
//        logger.debug("生成的最终签名：");
//        logger.debug(signature);

        return signature;
    }

    /**
     * 通过String装载私钥
     * @param keyData
     * @return
     * @throws Exception
     */
	private static PrivateKey getPrivateKey(final String keyData) throws Exception {
        byte[] encodedKey = Base64.decodeBase64(keyData);
//        byte[] encodedKey = Base64.decode(keyData);
        final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey);
        final KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
        return keyFactory.generatePrivate(keySpec);
	}
	
	/**
	 * 通过公钥文件生成RSA公钥
	 * @param path
	 * @return
	 */
	public static RSAPublicKey getRSAPubKey(String path){
		return getRSAPubKey(readPemKey(path));
	}
	
	/**
	 * 通过公钥串生成RSA公钥
	 * @param bytes 通过decodeBase64()
	 * @return
	 */
	public static RSAPublicKey getRSAPubKey(byte[] bytes){
		try {
			KeySpec keySpec = new X509EncodedKeySpec(bytes);
			KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM_RSA);
			return (RSAPublicKey) keyFactory.generatePublic(keySpec);
		} catch (Exception e) {
			return null;
		}
	}
	
	/**
	 * MD5加密-指定编码方式
	 * @param content
	 * @param encoding
	 * @return
	 */
	public static String encodeMd5(String content, String encoding){
    	try {
			return DigestUtils.md5Hex(content.getBytes(encoding)).toUpperCase();
		} catch (UnsupportedEncodingException e) {
			return null;
		}
    }
	
	/**
	 * MD5加密  默认UTF-8
	 * @param content
	 * @return
	 */
	public static String encodeMd5(String content){
    	return encodeMd5(content, ENCODING);
    }
	
	/**
	 * RSA公钥验签
	 * @param message
	 * @param signature
	 * @param publicKey
	 * @return
	 */
	public static boolean designRSA(String message, byte[] signature, RSAPublicKey publicKey){
		return _designRSA(message, signature, publicKey, KEY_SIGNATURE_RSA);
	}
	public static boolean designRSA1(String message, byte[] signature, RSAPublicKey publicKey ){
		return _designRSA(message, signature, publicKey, KEY_SIGNATURE_RSA1);
	}

	private static boolean _designRSA(String message, byte[] signature, RSAPublicKey publicKey, String algorithm) {
		boolean flag = false;
		try {
			Signature sign = Signature.getInstance(algorithm);
			sign.initVerify(publicKey);
			sign.update(message.getBytes(ENCODING));
			flag = sign.verify(signature);
		} catch (Exception e) {
		}
		return flag;
	}
	
	/**
	 * 读取PEM文件
	 * @param path
	 * @return
	 */
	private static byte[] readPemKey(String path){
		path = CipherUtils.class.getResource(path).getFile();
        try (FileInputStream fileInputStream = new FileInputStream(path);) {
            FileChannel fileChannel = fileInputStream.getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocate((int) fileChannel.size());
            fileChannel.read(byteBuffer);
            byteBuffer.flip();
            String key = new String(byteBuffer.array(), ENCODING);
            if (key.contains("-----BEGIN PRIVATE KEY-----")) {
               return Utils.decodeBase64(key.replaceAll("-----\\w+ PRIVATE KEY-----", ""));
            } else if (key.contains("-----BEGIN PUBLIC KEY-----")) {
            	return Utils.decodeBase64(key.replaceAll("-----\\w+ PUBLIC KEY-----", ""));
            } else if (key.contains("-----BEGIN RSA PRIVATE KEY-----")) {
                final byte[] innerKey = Utils.decodeBase64(key.replaceAll("-----\\w+ RSA PRIVATE KEY-----", ""));
                byte[] bytes = new byte[innerKey.length + 26];
                System.arraycopy(Utils.decodeBase64("MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKY="), 0, bytes, 0, 26);
                System.arraycopy(BigInteger.valueOf(bytes.length - 4).toByteArray(), 0, bytes, 2, 2);
                System.arraycopy(BigInteger.valueOf(innerKey.length).toByteArray(), 0, bytes, 24, 2);
                System.arraycopy(innerKey, 0, bytes, 26, innerKey.length);
                return bytes;
            } else {
    			return null;
    		}
        } catch (Exception e) {
			return null;
		}
	}
	
}
